using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// \class  gkEventComponent
/// \brief  Component for event raising and handling
[AddComponentMenu("GK/Engine/Core/Event/gkEventComponent")]
public class gkEventComponent : MonoBehaviour 
{	
	/// The Event handler prototype
	public delegate void EventHandler(Object a_rEvent);
	
	/// The event handlers
	private MultiValueDictionary<string, EventHandler> m_oEventHandlers = new MultiValueDictionary<string, EventHandler>();
	
	/// The event hanlders to remove after an event have raised handlers
	private List<KeyValuePair<string, EventHandler>> m_oEvenHandlersToRemove = new List<KeyValuePair<string, EventHandler>>();
	
	/// Are we raising an event
	private int m_iRaisingEventCount;
	
	/// The delayed events
	private List<gkDelayedEvent> m_oDelayedEvents = new List<gkDelayedEvent>();
	
	/// The delayed events
	private List<gkDelayedEvent> m_oDelayedEventsToRemove = new List<gkDelayedEvent>();
	
	/// \brief  Called each frame
	void Update()
	{
		// Update the delayed events
		UpdateDelayedEvents();
	}
	
	/// \brief  Add an event handler
	public void AddEventHandler(string a_rEventName, EventHandler a_rEventHandler)
	{
		m_oEventHandlers.Add(a_rEventName, a_rEventHandler);
	}
	
	/// Remove an event handler
	public void RemoveEventHandler(string a_rEventName, EventHandler a_rEventHandler)
	{
		// If raising an event wai until the end of the event raise to remove the event handler
		if(m_iRaisingEventCount > 0)
		{
			m_oEvenHandlersToRemove.Add(new KeyValuePair<string, EventHandler>(a_rEventName, a_rEventHandler));
		}
		else
		{
			m_oEventHandlers.Remove(a_rEventName, a_rEventHandler);
		}
	}
	
	/// \brief Raise an event
	public void RaiseEvent(string a_rEventName, Object a_rEvent = null)
	{
		m_iRaisingEventCount++;
	
		HashSet<EventHandler> rNamedEventHandlers = m_oEventHandlers.GetValues(a_rEventName);
		
		if(rNamedEventHandlers != null)
		{
			// Loop through the handler 
			foreach(EventHandler rEventHandler in rNamedEventHandlers)
			{
				// Call the handler on the raising event
				rEventHandler(a_rEvent);
			}
		}
	
		RemoveEventHandlers();
		
		m_iRaisingEventCount--;
	}
	
	/// \brief Raise an event with delay
	public void RaiseDelayedEvent(float a_fDelay, string a_rEventName, Object a_rEvent = null)
	{
		if(a_fDelay > 0.0f)
		{
			m_oDelayedEvents.Add(new gkDelayedEvent(a_fDelay, a_rEventName, a_rEvent));
		}
		else
		{
			RaiseEvent(a_rEventName, a_rEvent);
		}
	}
	
	/// \brief Update the delayed events
	void UpdateDelayedEvents()
	{
		// Loop through the delayed events and update them
		foreach(gkDelayedEvent rDelayedEvent in m_oDelayedEvents)
		{
			// Update the delayed event count down and test if it's finished 
			if(rDelayedEvent.CountDownTimeBeforeEvent(Time.deltaTime))
			{
				// Raise the finished delayed event
				RaiseEvent(rDelayedEvent.GetEventName(), rDelayedEvent.GrabEvent());
				
				// Stack the finished delayed event to remove
				m_oDelayedEventsToRemove.Add(rDelayedEvent);
			}
		}
		
		// Remove the finished delayed event
		RemoveDelayedEvents();
	}
	
	/// \brief  Remove the delayed events
	void RemoveDelayedEvents()
	{
		// Loop through the delayed events to remove and remove them from the delayed events
		foreach(gkDelayedEvent rDelayedEventToRemove in m_oDelayedEventsToRemove)
		{
			m_oDelayedEvents.Remove(rDelayedEventToRemove);
		}
	}
	
	/// \brief  Remove the events for wich we wait until the 
	void RemoveEventHandlers()
	{
		if(m_iRaisingEventCount <= 1)
		{
			// Loop through the handler to remove 
			foreach(KeyValuePair<string, EventHandler> rEventNameAndHandlerToRemove in m_oEvenHandlersToRemove)
			{
				// Call the handler on the raising event
				m_oEventHandlers.Remove(rEventNameAndHandlerToRemove.Key, rEventNameAndHandlerToRemove.Value);
			}
	
			// Clear the array
			m_oEvenHandlersToRemove.Clear();
		}
	}
}
